﻿#include "COrderManager.h"
#include <random>
#include <iostream>
#include <sstream>
#include <iomanip>

#include <QDateTime>
#include <QSqlQuery>
#include <QVariant>
#include "glog/logging.h"

#include "CDBConnector.h"

using namespace zl;

#define CURRENT_DATESTR QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss").toStdString()

COrderManager* COrderManager::Instance_ = nullptr;
CabinetInfo COrderManager::Config_;

COrderManager * zl::COrderManager::Instance()
{
	if (Instance_ == nullptr)
	{
		Instance_ = new COrderManager;
	}

	return Instance_;
}

void zl::COrderManager::Release()
{
	LOG(WARNING) << "release order manager...";
	delete Instance_;
	Instance_ = nullptr;
}

std::string zl::COrderManager::GeneratedOrderID()
{
	std::default_random_engine e;
	e.seed(QDateTime::currentDateTime().toMSecsSinceEpoch());

	std::ostringstream oss;
    oss << Config_.id;
	oss << QDateTime::currentDateTime().toString("yyyyMMddhhmmss").toStdString();
	oss << std::setw(10) << std::setfill('0') << e();

	return oss.str();
}

int32_t zl::COrderManager::Init(CabinetInfo config)
{
    Config_ = config;
	return EResult_Success;
}

Order zl::COrderManager::CreateOrder()
{
	LOG(INFO) << "create order";
	Order order;
	order.order_id = GeneratedOrderID();
	order.status = zl::EOrderStatus_Prepared;
	order.begin_time = "";
    order.end_time = "";
    order.exp_time = "";
	order.update_time = "";
    order.cabinet_no = Config_.id;
	order.box_no = 0;
    order.box_type = 0;
	order.user_id = "";
	order.user_code = "";
    order.confirmed = 0;
    order.facial_flag = 0;

	return order;
}

int32_t zl::COrderManager::BeginOrder(Order & order)
{
    LOG(INFO) << "begin order: " << order.order_id;
    auto tmp = calculate_comfirm_flag(0, 0x01, true);

	QSqlQuery query = zl::CDBConnector::Instance()->CreateQuery();
	query.prepare("INSERT INTO T_ORDER (ORDER_ID, STATUS, BEGIN_TIME,\
        UPDATE_TIME, EXP_TIME, CABINET_NO, BOX_NO, BOX_TYPE, USER_ID, \
		USER_CODE, CONFIRMED) VALUES(:order_id, :status, :begin_time, \
        :update_time, :exp_time, :cabinet_no, :box_no, :box_type, \
        :user_id, :user_code, :confirmed)");
	query.bindValue(":order_id", order.order_id.c_str());
    query.bindValue(":status", EOrderStatus_Using);
	query.bindValue(":begin_time", CURRENT_DATESTR.c_str());
	query.bindValue(":update_time", CURRENT_DATESTR.c_str());
    query.bindValue(":exp_time", order.exp_time.c_str());
	query.bindValue(":cabinet_no", order.cabinet_no.c_str());
	query.bindValue(":box_no", order.box_no);
    query.bindValue(":box_type", order.box_type);
	query.bindValue(":user_id", order.user_id.c_str());
	query.bindValue(":user_code", order.user_code.c_str());
    query.bindValue(":confirmed", tmp);

	int32_t ret = zl::CDBConnector::Instance()->ExecQuery(query);
	if (ret != zl::CDBConnector::EDBError_Success)
	{
        LOG(ERROR) << "begin order failed: " << ret;
		return EResult_Failed;
	}

    order.status = EOrderStatus_Using;
	order.begin_time = CURRENT_DATESTR;
	order.update_time = CURRENT_DATESTR;
    order.exp_time = QDateTime::currentDateTime().addDays(1).toString("yyyy-MM-dd hh:mm:ss").toStdString();
    order.confirmed = tmp;

    LOG(INFO) << "begin order success: " << ret;
	return EResult_Success;
}


int32_t zl::COrderManager::FinishOrder(Order& order)
{
	LOG(INFO) << "finish order: " << order.order_id;
    auto tmp = calculate_comfirm_flag(order.confirmed, 0x02, true);

	QSqlQuery query = zl::CDBConnector::Instance()->CreateQuery();
	query.prepare("UPDATE T_ORDER SET STATUS=:status, \
		END_TIME=:end_time, UPDATE_TIME=:update_time, CONFIRMED=:flag \
		WHERE ORDER_ID=:order_id");
	query.bindValue(":status", EOrderStatus_Finished);
	query.bindValue(":end_time", CURRENT_DATESTR.c_str());
	query.bindValue(":update_time", CURRENT_DATESTR.c_str());
    query.bindValue(":flag", tmp);
	query.bindValue(":order_id", order.order_id.c_str());

	int32_t ret = zl::CDBConnector::Instance()->ExecQuery(query);
	if (ret != zl::CDBConnector::EDBError_Success)
	{
		LOG(ERROR) << "finish order failed: " << ret;
		return EResult_Failed;
	}

	order.status = EOrderStatus_Finished;
	order.end_time = CURRENT_DATESTR.c_str();
    order.update_time = CURRENT_DATESTR.c_str();
    order.confirmed = tmp;

	LOG(INFO) << "finish order success: " << order.order_id;
	return EResult_Success;
}

int32_t zl::COrderManager::LockOrder(Order& order)
{
	// todo
	return int32_t();
}

int32_t zl::COrderManager::CancelOrder(Order& order)
{
	LOG(INFO) << "cancel order: " << order.order_id;
    auto tmp = calculate_comfirm_flag(order.confirmed, 0x02, true);

	QSqlQuery query = zl::CDBConnector::Instance()->CreateQuery();
	query.prepare("UPDATE T_ORDER SET STATUS=:status, END_TIME=:endtime, \
		UPDATE_TIME=:update_time, CONFIRMED=:flag WHERE ORDER_ID=:order_id");
	query.bindValue(":status", EOrderStatus_ManualFinished);
	query.bindValue(":update_time", CURRENT_DATESTR.c_str());
	query.bindValue(":endtime", CURRENT_DATESTR.c_str());
    query.bindValue(":flag", tmp);
	query.bindValue(":order_id", order.order_id.c_str());

	int32_t ret = zl::CDBConnector::Instance()->ExecQuery(query);
	if (ret != zl::CDBConnector::EDBError_Success)
	{
		LOG(ERROR) << "cancel order failed: " << ret;
		return EResult_Failed;
	}

	order.status = EOrderStatus_ManualFinished;
	order.update_time = CURRENT_DATESTR;
	order.end_time = CURRENT_DATESTR;
    order.confirmed = tmp;

	LOG(INFO) << "cancel order success: " << order.order_id;
	return EResult_Success;
}

int32_t zl::COrderManager::ConfirmOrder(const std::string & order_id, int32_t type)
{
    LOG(INFO) << "confirm order: " << order_id;

    // get order confirmed value
    zl::Order order;
    auto ret1 = this->GetOrderInfo(order_id, order);
    if (ret1 != zl::EResult_Success)
    {
        LOG(ERROR) << "confirm order failed, order not found: " << order_id;
        return EResult_Failed;
    }
    int32_t tmp = calculate_comfirm_flag(order.confirmed, type, false);

    // update order
	QSqlQuery query = zl::CDBConnector::Instance()->CreateQuery();
	query.prepare("UPDATE T_ORDER SET CONFIRMED=:flag WHERE ORDER_ID=:orderid");
    query.bindValue(":flag", tmp);
	query.bindValue(":orderid", order_id.c_str());

	auto ret = zl::CDBConnector::Instance()->ExecQuery(query);
	if (ret != zl::CDBConnector::EDBError_Success)
	{
		LOG(ERROR) << "confirm order failed:" << ret;
		return EResult_Failed;
	}

	LOG(INFO) << "confirm order success: " << order_id;
	return EResult_Success;
}

int32_t zl::COrderManager::GetOrderInfo(const std::string & order_id, Order & order)
{
	LOG(INFO) << "get order info: " << order_id;
	QSqlQuery query = zl::CDBConnector::Instance()->CreateQuery();
	query.prepare("SELECT * FROM T_ORDER WHERE ORDER_ID=:order_id");
	query.bindValue(":order_id", order_id.c_str());

	int32_t ret = zl::CDBConnector::Instance()->ExecQuery(query);
	if (ret != zl::CDBConnector::EDBError_Success || !query.next())
	{
		LOG(ERROR) << "order information not found or query failed: " << ret;
		return EResult_Failed;
	}

	order.order_id = order_id;
	order.status = query.value("STATUS").toInt();
	order.begin_time = query.value("BEGIN_TIME").toString().toStdString();
    order.end_time = query.value("END_TIME").toString().toStdString();
	order.update_time = query.value("UPDATE_TIME").toString().toStdString();
    order.exp_time = query.value("EXP_TIME").toString().toStdString();
	order.cabinet_no = query.value("CABINET_NO").toString().toStdString();
	order.box_no = query.value("BOX_NO").toInt();
    order.box_type = query.value("BOX_TYPE").toInt();
	order.user_id = query.value("USER_ID").toString().toStdString();
	order.user_code = query.value("USER_CODE").toString().toStdString();
	order.confirmed = query.value("CONFIRMED").toInt();
	LOG(INFO) << "get order info success: " << order_id;
	return EResult_Success;
}

int32_t zl::COrderManager::GetAllOrders(
	OrderVec & vec, 
	const std::string & begin_time, 
	const std::string & end_time, 
	const std::string & phone, 
	const int32_t order_status, 
	const int32_t order_type,
	const int32_t lockerno)
{
	LOG(INFO) << "query order";
	QSqlQuery query = zl::CDBConnector::Instance()->CreateQuery();
    QString str = "SELECT * FROM T_ORDER WHERE BEGIN_TIME>=:begin_time AND BEGIN_TIME<=:end_time";
	if (!phone.empty())
	{
		str += " AND USER_ID=:phone";
	}
	if (order_status > 0)
	{
        str += " AND STATUS=:status";
	}
	if (lockerno > 0)
	{
		str += " AND BOX_NO=:box_no";
	}
    if (order_type == 0)
	{
        str += " AND CONFIRMED=:flag";
	}
    if (order_type == 1)
    {
        str += " AND (CONFIRMED=1 OR CONFIRMED=3)";
    }
	str += " ORDER BY UPDATE_TIME DESC";

	query.prepare(str);
	query.bindValue(":begin_time", begin_time.c_str());
	query.bindValue(":end_time", end_time.c_str());
	query.bindValue(":phone", phone.c_str());
    query.bindValue(":status", order_status);
	query.bindValue(":box_no", lockerno);
    query.bindValue(":flag", order_type);

	int32_t ret = zl::CDBConnector::Instance()->ExecQuery(query);
	if (ret != zl::CDBConnector::EDBError_Success)
	{
		LOG(ERROR) << "get all order failed: " << ret;
		return EResult_Failed;
	}

	while (query.next())
	{
		Order order;	
		order.order_id = query.value("ORDER_ID").toString().toStdString();
		order.status = query.value("STATUS").toInt();
		order.begin_time = query.value("BEGIN_TIME").toString().toStdString();
        order.end_time = query.value("END_TIME").toString().toStdString();
        order.exp_time = query.value("EXP_TIME").toString().toStdString();
		order.update_time = query.value("UPDATE_TIME").toString().toStdString();
		order.cabinet_no = query.value("CABINET_NO").toString().toStdString();
		order.box_no = query.value("BOX_NO").toInt();
        order.box_type = query.value("BOX_TYPE").toInt();
		order.user_id = query.value("USER_ID").toString().toStdString();
		order.user_code = query.value("USER_CODE").toString().toStdString();
		order.confirmed = query.value("CONFIRMED").toInt();
		vec.push_back(order);
	}

	LOG(INFO) << "get all orders success: " << vec.size();
	return EResult_Success;
}

int32_t zl::COrderManager::GetOrderByUser(Order & order, 
	const std::string & userid, const std::string & pwd)
{
	LOG(INFO) << "get order by user: " << userid;
	QSqlQuery query = zl::CDBConnector::Instance()->CreateQuery();
	if (pwd.empty())
	{
		query.prepare("SELECT * FROM T_ORDER WHERE USER_ID=:userid  \
			AND ( STATUS=1 OR STATUS=2) ORDER BY BEGIN_TIME DESC");
		query.bindValue(":userid", userid.c_str());
	}
	else
	{
		query.prepare("SELECT * FROM T_ORDER WHERE USER_ID=:userid \
			AND USER_CODE=:usercode AND (STATUS=1 OR STATUS=2) \
			ORDER BY BEGIN_TIME DESC");
		query.bindValue(":userid", userid.c_str());
		query.bindValue(":usercode", pwd.c_str());
	}

	int32_t ret = zl::CDBConnector::Instance()->ExecQuery(query);
	if (ret != zl::CDBConnector::EDBError_Success || !query.next())
	{
		LOG(ERROR) << "order information not found or query failed: " << ret;
		return EResult_Failed;
	}

	order.order_id = query.value("ORDER_ID").toString().toStdString();
	order.status = query.value("STATUS").toInt();
	order.begin_time = query.value("BEGIN_TIME").toString().replace('T', ' ').toStdString();
    order.end_time = query.value("END_TIME").toString().replace('T', ' ').toStdString();
	order.update_time = query.value("UPDATE_TIME").toString().replace('T', ' ').toStdString();
    order.exp_time = query.value("EXP_TIME").toString().replace('T', ' ').toStdString();
	order.cabinet_no = query.value("CABINET_NO").toString().toStdString();
	order.box_no = query.value("BOX_NO").toInt();
    order.box_type = query.value("BOX_TYPE").toInt();
	order.user_id = query.value("USER_ID").toString().toStdString();
	order.user_code = query.value("USER_CODE").toString().toStdString();
	order.confirmed = query.value("CONFIRMED").toInt();

	return EResult_Success;
}

int32_t zl::COrderManager::GetOrderByBoxno(Order & order, int32_t boxno)
{
	LOG(INFO) << "get order by boxno: " << boxno;
	QSqlQuery query = zl::CDBConnector::Instance()->CreateQuery();
	query.prepare("SELECT * FROM T_ORDER WHERE BOX_NO=:boxno AND \
		(STATUS=:s1 OR STATUS=:s2) ORDER BY UPDATE_TIME DESC");
	query.bindValue(":boxno", boxno);
	query.bindValue(":s1", zl::EOrderStatus_Prepared);
	query.bindValue(":s2", zl::EOrderStatus_Using);

	int32_t ret = zl::CDBConnector::Instance()->ExecQuery(query);
	if (ret != zl::CDBConnector::EDBError_Success || !query.next())
	{
		LOG(ERROR) << "order information not found or query failed: " << ret;
		return EResult_Failed;
	}

	order.order_id = query.value("ORDER_ID").toString().toStdString();
	order.status = query.value("STATUS").toInt();
	order.begin_time = query.value("BEGIN_TIME").toString().replace('T', ' ').toStdString();
    order.end_time = query.value("END_TIME").toString().replace('T', ' ').toStdString();
	order.update_time = query.value("UPDATE_TIME").toString().replace('T', ' ').toStdString();
    order.exp_time = query.value("EXP_TIME").toString().replace('T', ' ').toStdString();
	order.cabinet_no = query.value("CABINET_NO").toString().toStdString();
	order.box_no = query.value("BOX_NO").toInt();
    order.box_type = query.value("BOX_TYPE").toInt();
	order.user_id = query.value("USER_ID").toString().toStdString();
	order.user_code = query.value("USER_CODE").toString().toStdString();
	order.confirmed = query.value("CONFIRMED").toInt();

	return EResult_Success;
}

int32_t zl::COrderManager::GetAllUnconfirmedOrders(OrderVec& orders)
{
    // LOG(INFO) << "get unconfirmed orders";
	QSqlQuery query = zl::CDBConnector::Instance()->CreateQuery();
    query.prepare("SELECT * FROM T_ORDER WHERE CONFIRMED<>:flag");
    query.bindValue(":flag", 0);


	int32_t ret = zl::CDBConnector::Instance()->ExecQuery(query);
	if (ret != zl::CDBConnector::EDBError_Success)
	{
		LOG(ERROR) << "order information not found or query failed: " << ret;
		return EResult_Failed;
	}

	while (query.next())
	{
		Order order;
		order.order_id = query.value("ORDER_ID").toString().toStdString();
		order.status = query.value("STATUS").toInt();
		order.begin_time = query.value("BEGIN_TIME").toString().replace('T', ' ').toStdString();
        order.end_time = query.value("END_TIME").toString().replace('T', ' ').toStdString();
		order.update_time = query.value("UPDATE_TIME").toString().replace('T', ' ').toStdString();
        order.exp_time = query.value("EXP_TIME").toString().replace('T', ' ').toStdString();
		order.cabinet_no = query.value("CABINET_NO").toString().toStdString();
		order.box_no = query.value("BOX_NO").toInt();
        order.box_type = query.value("BOX_TYPE").toInt();
		order.user_id = query.value("USER_ID").toString().toStdString();
		order.user_code = query.value("USER_CODE").toString().toStdString();
		order.confirmed = query.value("CONFIRMED").toInt();
		orders.push_back(order);
	}

	return EResult_Success;
}

int32_t zl::COrderManager::GetAllUsingOrders(OrderVec& orders)
{
    // LOG(INFO) << "get unconfirmed orders";
	QSqlQuery query = zl::CDBConnector::Instance()->CreateQuery();

	query.prepare("SELECT * FROM T_ORDER WHERE STATUS=:s1");
	query.bindValue(":s1", zl::EOrderStatus_Using);

	int32_t ret = zl::CDBConnector::Instance()->ExecQuery(query);
	if (ret != zl::CDBConnector::EDBError_Success)
	{
		LOG(ERROR) << "order information not found or query failed: " << ret;
		return EResult_Failed;
	}

	while (query.next())
	{
		Order order;
		order.order_id = query.value("ORDER_ID").toString().toStdString();
		order.status = query.value("STATUS").toInt();
		order.begin_time = query.value("BEGIN_TIME").toString().replace('T', ' ').toStdString();
        order.end_time = query.value("END_TIME").toString().replace('T', ' ').toStdString();
		order.update_time = query.value("UPDATE_TIME").toString().replace('T', ' ').toStdString();
        order.exp_time = query.value("EXP_TIME").toString().replace('T', ' ').toStdString();
		order.cabinet_no = query.value("CABINET_NO").toString().toStdString();
		order.box_no = query.value("BOX_NO").toInt();
        order.box_type = query.value("BOX_TYPE").toInt();
		order.user_id = query.value("USER_ID").toString().toStdString();
		order.user_code = query.value("USER_CODE").toString().toStdString();
		order.confirmed = query.value("CONFIRMED").toInt();
		orders.push_back(order);
	}

	return EResult_Success;
}

int32_t COrderManager::calculate_comfirm_flag(int32_t value, int32_t type, bool flag)
{
    if (flag)
    {
        return value | type;
    }
    else
    {
        return value & (~type);
    }

}
